/*
 * Copyright (c) 2007 Compl Yue Still, All Rights Reserved.
 */

package hbi.wire.as3.host
{
	import flash.events.*;
	import flash.net.Socket;
	import flash.net.URLStream;
	import flash.utils.*;
	
	import hbi.domain.as3.AS3Gateway;
	import hbi.task.as3.TaskAgentLifeScript;
	import hbi.wire.ProtocolError;
	
	import mx.controls.Alert;
	
	public class HostEnd
	{
		
		private var _gateway:AS3Gateway;
		
		
		public function HostEnd(gateway:AS3Gateway)
		{
			this._gateway = gateway;
		}
		
		public function hostTaskAgent(agentScript:TaskAgentLifeScript):void
		{
			// TODO implement retry as appropriate
			agentScript.liveUp(_gateway);
		}
		
		private var input:IDataInput;
		
		public function bindSocket(socket:Socket):void
		{
			this.input = socket;
			socket.addEventListener(ProgressEvent.SOCKET_DATA, readData);
			socket.addEventListener(Event.CLOSE, closed);
			socket.addEventListener(IOErrorEvent.IO_ERROR, ioError);
		}
		
		public function bindStream(stream:URLStream):void
		{
			this.input = stream;
			stream.addEventListener(ProgressEvent.PROGRESS, readData);
			stream.addEventListener(Event.CLOSE, closed);
			stream.addEventListener(IOErrorEvent.IO_ERROR, ioError);
		}
		
		private function closed(event:Event):void
		{
			Alert.show("HBI connection closed!", "HBI");
		}

		private function ioError(event:Event):void
		{
			Alert.show("HBI connection error!", "HBI");
		}
		
		[ArrayElementType("hbi.wire.as3.host::ResolvedType")]
		private var resolvedTypes:Array = new Array();
		
		internal function resolveType(typeName:String):void
		{
			var pos:int = typeName.lastIndexOf(".");
			if(pos > 0) typeName = typeName.substring(0, pos) + 
				"::" + typeName.substring(pos+1);
			trace("Resolving type: " + typeName);
			var as3Class:Class = getDefinitionByName(typeName) as Class;
			if(!as3Class)
				throw new ProtocolError("Unresolvable type: " + typeName);
			resolvedTypes.push(new ResolvedType(as3Class));
		}
		
		internal function getResolvedType(typeIndex:int):ResolvedType
		{
			return resolvedTypes[typeIndex] as ResolvedType;
		}
		
		private var incomingTag:IncomingTag = null;
		
		private function readData(dataEvent:ProgressEvent):void
		{
			tag_scan: while(input.bytesAvailable > 0)
			{
				if(incomingTag == null)
				{
					var tag:uint = input.readUnsignedByte();
					switch(tag)
                    // this switch defines top-level tags
                    {
                    case 't'.charCodeAt(): // type constant
                        incomingTag = new TypeConstant(this);
                        break;
                    case 'a'.charCodeAt(): // task agent life script
                        incomingTag = new TaskScript(this);
                        break;
                    case 'A'.charCodeAt(): // streaming task agent life script
                        throw new Error("Not implemented yet");
                    case 0x7F: // DEL
                        resolvedTypes = new Array();
                        continue tag_scan;
                    default:
                        throw new ProtocolError("Bad tag: " + tag);
					}
				}
                if (incomingTag.processIncomingData(input))
                {
                    incomingTag = null;
                }
			}
			
			if(!Object(input).connected && incomingTag != null)
			// insufficient input data to finish current tag
                throw new ProtocolError("Incomplete tag");
		}
		
	}
}
